#include "model/der/Relation.h"
#include <iostream>
#include <string>
#include <sstream>
#include "gui/Dibujar/Defines.h"
#include "model/Connector.h"
#include "model/der/ViaDER.h"
#include "model/der/Entity.h"
#include "model/der/Attribute.h"

EntityRelationLinkInfo::EntityRelationLinkInfo(Entity *entity, std::string viaType, Connector *conectorDeLaEntidad, Connector *conectorDeLaRelacion) {
	this->entity = entity;
	this->conectorDeLaEntidad = conectorDeLaEntidad;
	this->conectorDeLaRelacion = conectorDeLaRelacion;

	if (viaType.compare("0..1") == 0) {
		typeOfRelation = TypeOfRelation0pp1;
	}
	if (viaType.compare("1..1") == 0) {
		typeOfRelation = TypeOfRelation1pp1;
	}
	if (viaType.compare("0..N") == 0) {
		typeOfRelation = TypeOfRelation0ppN;
	}
	if (viaType.compare("1..N") == 0) {
		typeOfRelation = TypeOfRelation1ppN;
	}
	if (viaType.compare("N..N") == 0) {
		typeOfRelation = TypeOfRelationNppN;
	}
}

std::string EntityRelationLinkInfo::description() {
	std::string d = "";
	d += entity->getName() + " (";
	switch (typeOfRelation) {
	case TypeOfRelation0pp1:
		d += "O..1";
		break;
	case TypeOfRelation1pp1:
		d += "1..1";
		break;
	case TypeOfRelation0ppN:
		d += "O..N";
		break;
	case TypeOfRelation1ppN:
		d += "1..N";
		break;
	case TypeOfRelationNppN:
		d += "N..N";
		break;
	}
	d += ")";
	return d;
}

//EntityEntityLinkInfo::EntityEntityLinkInfo(Entity *entity1, Connector *conector1, Connector *conector2, Entity *entity2, std::string viaType) {
//	this->entity1 = entity1;
//	this->entity2 = entity2;
//	this->conector1 = conector1;
//	this->conector2 = conector2;
//
//	if (viaType.compare("0..1") == 0) {
//		typeOfRelation = TypeOfRelation0pp1;
//	}
//	if (viaType.compare("1..1") == 0) {
//		typeOfRelation = TypeOfRelation1pp1;
//	}
//	if (viaType.compare("0..N") == 0) {
//		typeOfRelation = TypeOfRelation0ppN;
//	}
//	if (viaType.compare("1..N") == 0) {
//		typeOfRelation = TypeOfRelation1ppN;
//	}
//	if (viaType.compare("N..N") == 0) {
//		typeOfRelation = TypeOfRelationNppN;
//	}
//}
//
//std::string EntityEntityLinkInfo::description() {
//	std::string d = "";
//	d += entity1->getName() + " con "+ entity2->getName();
//	switch (typeOfRelation) {
//	case TypeOfRelation0pp1:
//		d += "O..1";
//		break;
//	case TypeOfRelation1pp1:
//		d += "1..1";
//		break;
//	case TypeOfRelation0ppN:
//		d += "O..N";
//		break;
//	case TypeOfRelation1ppN:
//		d += "1..N";
//		break;
//	case TypeOfRelationNppN:
//		d += "N..N";
//		break;
//	}
//	d += ")";
//	return d;
//}

Relation::Relation(const std::string & name, int orientation)
: ComponentDER(name, orientation){

	initializeConnectors();
}

Relation::Relation(const Relation & otro) : ComponentDER(otro){

	initializeConnectors();
}


Relation& Relation::operator=(const Relation& otro) {

	ComponentDER::operator=(otro);
	initializeConnectors();
	return *this;
}

void Relation::initializeConnectors() {
	componentType = ComponentTypeDerRelation;

	addConnector(RELATION_ENTRADA_0, true);
	addConnector(RELATION_ENTRADA_01, true);
	addConnector(RELATION_ENTRADA_02, true);
	addConnector(RELATION_ENTRADA_03, true);
	addConnector(RELATION_ENTRADA_04, true);
	addConnector(RELATION_ENTRADA_1, true);
	addConnector(RELATION_ENTRADA_11, true);
	addConnector(RELATION_ENTRADA_12, true);
	addConnector(RELATION_ENTRADA_13, true);
	addConnector(RELATION_ENTRADA_14, true);
	addConnector(RELATION_ENTRADA_2, true);
	addConnector(RELATION_ENTRADA_21, true);
	addConnector(RELATION_ENTRADA_22, true);
	addConnector(RELATION_ENTRADA_23, true);
	addConnector(RELATION_ENTRADA_24, true);
	addConnector(RELATION_ENTRADA_3, true);
	addConnector(RELATION_ENTRADA_31, true);
	addConnector(RELATION_ENTRADA_32, true);
	addConnector(RELATION_ENTRADA_33, true);
	addConnector(RELATION_ENTRADA_34, true);
	m_types.push_back("Fuerte");
	m_types.push_back("Debil");
}

void Relation::serialize(XmlWriter & writer_rep, XmlWriter & writer_comp)
{
	writer_rep.addRootChild(TARGET_RELATION, "");
	ComponentDER::serialize(writer_rep, writer_comp);
}

void Relation::update()
{
	m_attributes.clear();
	m_entities.clear();
	_relationAttributes.clear();
	_relationCompositeAttributes.clear();
	_relationLinkedEntities.clear();

	for(unsigned i = 0; i < conectoresEntrantes.size(); i++)
		if(conectoresEntrantes[i]->getVia() != NULL) {
			ViaDER* via = (ViaDER *)conectoresEntrantes[i]->getVia();
			if(via->getEndConnector() != conectoresEntrantes[i]) {
				if(via->getEndConnector()->isEntry()) {
					Entity *entity = dynamic_cast<Entity*>(via->getEndConnector()->getFather());
					EntityRelationLinkInfo *relatedEntity = new EntityRelationLinkInfo(entity, via->getTypes()[via->getType()], via->getEndConnector(), via->getStartConnector());
					_relationLinkedEntities.push_back(relatedEntity);

					m_entities.push_back(entity);

				} else {
					ComponentDER *c = dynamic_cast<ComponentDER *>(via->getEndConnector()->getFather());
					m_attributes.push_back((Attribute *)c);

					switch (c->getComponentType()) {
						case ComponentTypeDerCompositeAttribute:
							_relationCompositeAttributes.push_back((CompositeAttribute *)c);
							break;
						case ComponentTypeDerAttribute:
							_relationAttributes.push_back((Attribute *)c);
							break;
						default:
							break;
					}
				}
			} else {
				if(via->getStartConnector()->isEntry()) {
					Entity *entity = dynamic_cast<Entity*>(via->getStartConnector()->getFather());
					EntityRelationLinkInfo *relatedEntity = new EntityRelationLinkInfo(entity, via->getTypes()[via->getType()], via->getStartConnector(), via->getEndConnector());
					_relationLinkedEntities.push_back(relatedEntity);
				} else {
					ComponentDER *c = dynamic_cast<ComponentDER *>(via->getStartConnector()->getFather());
					m_attributes.push_back((Attribute *)c);

					switch (c->getComponentType()) {
						case ComponentTypeDerCompositeAttribute:
							_relationCompositeAttributes.push_back((CompositeAttribute *)c);
							break;
						case ComponentTypeDerAttribute:
							_relationAttributes.push_back((Attribute *)c);
							break;
						default:
							break;
					}
					m_attributes.push_back((Attribute*)c);
				}
			}
		}
}

bool Relation::tieneAtributos() {
	update();
	return (_relationAttributes.size() + _relationCompositeAttributes.size()) >= 1;
}

std::string Relation::description() {
	this->update();

	std::string d = "";
	d += "RELATION: " + this->getName() + "[ ";
	for (unsigned int i = 0; i < _relationLinkedEntities.size(); i++) {
		EntityRelationLinkInfo *re = _relationLinkedEntities[i];
		d += re->description() + ", ";
	}
	d += " ] ( \n";
	for (unsigned int i = 0; i < _relationAttributes.size(); i++) {
		Attribute *a = _relationAttributes[i];
		d += "  " + a->description() + ",\n";
	}
	for (unsigned int i = 0; i < _relationCompositeAttributes.size(); i++) {
		CompositeAttribute *a = _relationCompositeAttributes[i];
		d += "  " + a->description() + ",\n";
	}
	d += ")\n";
	return d;
}
